/*
 * The MIT License
 *
 * Copyright 2012 http://generic-toolset.googlecode.com.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.rlpw.swing;

import java.util.Comparator;
import java.util.List;
import javax.swing.ComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.event.ListDataListener;

/**
 *
 * @param <T> the type of the elements
 */
public class JOComboBox<T> extends JComboBox<T> implements View<T>, Selectable<T> {

    private Model<T> model;
    private SelectModel<T> selected;
    private FilteredList<T> flist;
    private boolean nullable = false;

    public JOComboBox() {
        this(new DefaultModel<T>());
    }

    public JOComboBox(Model<T> m) {
        setDataModel(m);
        flist = new FilteredList<>(this);
        selected = new DefaultSelectModel<>(this);
        selected.setMode(SelectModel.SINGLE);

    }

    public boolean isNullable() {
        return nullable;
    }

    public void setNullable(boolean nullable) {
        this.nullable = nullable;
        updateView();
    }

    /** {@inheritDoc} */
    @Override
    public void addItem(T item) {
        getDataModel().add(item);
    }

    /** {@inheritDoc} */
    @Override
    public void insertItemAt(T item, int index) {
        getDataModel().add(flist.toDataModelIndex(index), item);
    }

    /** {@inheritDoc} */
    @Override
    public void removeItem(Object anObject) {
        getDataModel().remove((T) anObject);
    }

    /** {@inheritDoc} */
    @Override
    public void removeItemAt(int anIndex) {
        removeItem(flist.get(anIndex));
    }

    /** {@inheritDoc} */
    @Override
    public void removeAllItems() {
        getDataModel().clear();
    }

    /** {@inheritDoc} */
    @Override
    public T getSelectedItem() {
        return (T) super.getSelectedItem();
    }

    /** {@inheritDoc} */
    @Override
    public void updateView() {

        if (flist != null) {
            flist.update();
        }

        setModel(new ComboBoxModel<T>() {

            @Override
            public void setSelectedItem(Object anItem) {
                getSelectModel().setSelected((T) anItem, true);
            }

            @Override
            public Object getSelectedItem() {
                try {

                    if (getSelectModel().countSelected() > 0) {
                        return getSelectModel().getSelected().get(0);
                    } else if (!flist.isEmpty() && !isNullable()) {
                        setSelectedItem(flist.get(0));
                        return getSelectedItem();
                    }
                } catch (Exception excep) {
                }
                return null;
            }

            @Override
            public int getSize() {
                return flist.size() + (isNullable() ? 1 : 0);
            }

            @Override
            public T getElementAt(int index) {
                if (isNullable() && index == 0) {
                    return null;
                } else {
                    return flist.get(index - (isNullable() ? 1 : 0));
                }
            }

            @Override
            public void addListDataListener(ListDataListener l) {
            }

            @Override
            public void removeListDataListener(ListDataListener l) {
            }
        });

        try {
            if (!flist.contains(getSelectedItem())) {
                if (flist.isEmpty()) {
                    setSelectedItem(null);
                } else if (!isNullable()) {
                    setSelectedIndex(0);
                } else {
                    setSelectedItem(null);
                }
            }
        } catch (Exception excep) {
        }

        revalidate();
        repaint();
    }

    /** {@inheritDoc} */
    @Override
    public Model<T> getDataModel() {
        return model;
    }

    /** {@inheritDoc} */
    @Override
    public void setDataModel(Model<T> model) {
        if (model == null) {
            throw new IllegalArgumentException("Cannot set a null Model");
        }
        if (this.model != null) {
            this.model.removeView(this);
        }
        model.addView(this);
        this.model = model;
        updateView();
    }

    /** {@inheritDoc} */
    @Override
    public SelectModel<T> getSelectModel() {
        return selected;
    }

    /** {@inheritDoc} */
    @Override
    public Filter<T> getFilter() {
        return flist.getFilter();
    }

    /** {@inheritDoc} */
    @Override
    public void setFilter(Filter<T> filter) {
        flist.setFilter(filter);
    }

    /** {@inheritDoc} */
    @Override
    public void setSorter(Comparator<T> comp) {
        flist.setSorter(comp);
    }

    /** {@inheritDoc} */
    @Override
    public Comparator<T> getSorter() {
        return flist.getSorter();
    }

    /** {@inheritDoc} */
    @Override
    public List<T> getVisibleItems() {
        return flist;
    }
}
